package service

import (
	"encoding/json"
	"errors"
	"github.com/gin-gonic/gin"
	maasentity "github.com/maas/gomaasclient/entity"
	"github.com/maas/gomaasclient/entity/node"
	"gluenet/cmd/glue-srvm/app"
	config "gluenet/configs"
	model2 "gluenet/internal/glue-srvm/model"
	"gluenet/internal/glue-srvm/pkg/gluehttp"
	"gluenet/internal/glue-srvm/pkg/gluelog"
	"gluenet/internal/glue-srvm/storages"
	"gluenet/pkg/agent"
	corev1 "gluenet/pkg/agent/types"
	"gluenet/pkg/agent/v2/rpc"
	apis2 "gluenet/pkg/apis"
	"gluenet/pkg/database/mysql"
	"net/http"
	"sort"
	"strconv"
	"strings"
)

// TODO Some constants will be put into config later
const (
	TYPEVIRTUALCN        = "虚拟机"
	TYPEVMHOSTCN         = "裸机"
	TYPEVIRTUALEN        = "virtual"
	TYPEVMHOSTEN         = "vmhost"
	OPERATIONPOWERON     = "power_on"
	OPERATIONPOWEROFF    = "power_off"
	TAGREGION            = "Region"
	TAGPODCONSOLELOGGING = "pod-console-logging"
	TAGSEPARATOR         = "-"
)

const (
	MachineTypeVMHost = iota + 1
	MachineTypeHost
	MachineTypePhysicalMachine
)

// SliceRemoveDuplicates Remove duplicate elements in the []string
func SliceRemoveDuplicates(slice []string) []string {
	sort.Strings(slice)
	i := 0
	var j int
	for {
		if i >= len(slice)-1 {
			break
		}

		for j = i + 1; j < len(slice) && slice[i] == slice[j]; j++ {
		}
		slice = append(slice[:i+1], slice[j:]...)
		i++
	}
	return slice
}

// RemoveDuplicate Remove duplicate elements in the []int
func RemoveDuplicate(list *[]int) []int {
	var x []int = []int{}
	for _, i := range *list {
		if len(x) == 0 {
			x = append(x, i)
		} else {
			for k, v := range x {
				if i == v {
					break
				}
				if k == len(x)-1 {
					x = append(x, i)
				}
			}
		}
	}
	return x
}

// NodeStatusToStringStatus TODO Need to simplify mapping
func NodeStatusToStringStatus(status node.Status) string {
	switch status {
	case node.StatusNew:
		return "New"
	case node.StatusCommissioning:
		return "Commissioning"
	case node.StatusFailedCommissioning:
		return "Failed commissioning"
	case node.StatusMissing:
		return "Missing"
	case node.StatusReady:
		return "Ready"
	case node.StatusReserved:
		return "Reserved"
	case node.StatusDeployed:
		return "Deployed"
	case node.StatusRetired:
		return "Retired"
	case node.StatusBroken:
		return "Broken"
	case node.StatusDeploying:
		return "Deploying"
	case node.StatusAllocated:
		return "Allocated"
	case node.StatusFailedDeployment:
		return "Failed deployment"
	case node.StatusReleasing:
		return "Releasing"
	case node.StatusFailedReleasing:
		return "Releasing failed"
	case node.StatusDiskErasing:
		return "Disk erasing"
	case node.StatusFailedDiskErasing:
		return "Failed disk erasing"
	case node.StatusRescueMode:
		return "Rescue mode"
	case node.StatusEnteringRescureMode:
		return "Entering rescue mode"
	case node.StatusFailedEnteringRescueMode:
		return "Failed to enter rescue mode"
	case node.StatusExitingRescueMode:
		return "Exiting rescue mode"
	case node.StatusFailedExitingRescueMode:
		return "Failed to exit rescue mode"
	case node.StatusTesting:
		return "Testing"
	case node.StatusFailedTesting:
		return "Failed testing"
	}
	return "None"
}

//TODO Add some comments

// BootResourcesSearch Search boot resources
func BootResourcesSearch(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		var bootResource []map[string]string
		var resp model2.BootResourceResp
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		rlt, err := cli.Maas().BootResourcesSearch(&corev1.MaasReq{MaasURL: config.GetConfig().MaasUrl, APIKey: config.GetConfig().MaasAPIKey, Version: config.GetConfig().MaasAPIVersion})
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		_ = json.Unmarshal(rlt, &bootResource)
		// Data organization
		for _, v := range bootResource {
			if strings.Contains(v["architecture"], "amd64/generic") {
				resp.Architecture = append(resp.Architecture, v["architecture"])
			}
			if strings.Contains(v["architecture"], "amd") &&
				!strings.Contains(v["architecture"], "generic") {
				resp.MinimumKernel = append(resp.MinimumKernel, v["architecture"][strings.Index(v["architecture"], "/")+1:])
			}
		}
		gluehttp.Response(c, resp)
	}
}

// SubNetsSearch Search subnets
func SubNetsSearch(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		var resp []maasentity.Subnet
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		rlt, err := cli.Maas().SubNetsSearch(&corev1.MaasReq{MaasURL: config.GetConfig().MaasUrl, APIKey: config.GetConfig().MaasAPIKey, Version: config.GetConfig().MaasAPIVersion})
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		_ = json.Unmarshal(rlt, &resp)
		gluehttp.Response(c, resp)
	}
}

// VMHostsSearch Search vm hosts
func VMHostsSearch(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		var resp []maasentity.VMHost
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		rlt, err := cli.Maas().VMHostsSearch(&corev1.MaasReq{MaasURL: config.GetConfig().MaasUrl, APIKey: config.GetConfig().MaasAPIKey, Version: config.GetConfig().MaasAPIVersion})
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		_ = json.Unmarshal(rlt, &resp)
		gluehttp.Response(c, resp)
	}
}

// VMHostsCreate Create a virtual machine from the host
func VMHostsCreate(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		var resp map[string]string
		var val model2.VMHostsCreateReq
		var getParams string
		_ = c.Bind(&val)
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		cores := strconv.Itoa(val.Cores)
		memory := strconv.Itoa(val.Memory)
		storage := strconv.Itoa(val.Storage)
		// Choose available vm host
		for _, vmHostID := range val.VMHostID {
			// Search vm host
			var vmHost maasentity.VMHost
			id := strconv.Itoa(vmHostID)
			vmHostSearchReq := corev1.MaasReq{
				MaasURL:   config.GetConfig().MaasUrl,
				APIKey:    config.GetConfig().MaasAPIKey,
				Version:   config.GetConfig().MaasAPIVersion,
				GetParams: id,
			}
			rlt, err := cli.Maas().VMHostSearch(&vmHostSearchReq)
			if err != nil {
				gluelog.Errorf("failed to request rpc", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			_ = json.Unmarshal(rlt, &vmHost)
			// Judge whether resources are sufficient
			if vmHost.Available.Cores >= val.Cores &&
				vmHost.Available.Memory >= val.Memory &&
				vmHost.Available.LocalStorage >= val.Storage*1000000000 {
				getParams = id
				break
			}
		}
		if getParams == "" {
			err := errors.New("no available vm host")
			gluelog.Errorf("no available vm host", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		// Processing request
		massReq := corev1.MaasReq{
			MaasURL:   config.GetConfig().MaasUrl,
			APIKey:    config.GetConfig().MaasAPIKey,
			Version:   config.GetConfig().MaasAPIVersion,
			GetParams: getParams,
			Value: map[string]string{
				"hostname":     val.Hostname,
				"cores":        cores,
				"memory":       memory,
				"architecture": val.Architecture,
				"storage":      `test:` + storage + `(default)`,
				"interfaces":   `eth0:subnet_cidr=` + val.Interfaces,
			},
		}
		rlt, err := cli.Maas().VMHostsCreate(&massReq)
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		_ = json.Unmarshal(rlt, &resp)
		// Add tag to vm host
		for _, tag := range val.Tags {
			massReq := corev1.MaasReq{
				MaasURL:   config.GetConfig().MaasUrl,
				APIKey:    config.GetConfig().MaasAPIKey,
				Version:   config.GetConfig().MaasAPIVersion,
				GetParams: tag,
				Value: map[string]string{
					"add": resp["system_id"],
				},
			}
			_, err := cli.Maas().TagsUpdateNodes(&massReq)
			if err != nil {
				gluelog.Errorf("failed to request rpc", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
		}
		gluehttp.Response(c, resp)
	}
}

// VMHostsDelete Delete a virtual machine
func VMHostsDelete(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		var resp []maasentity.VMHost
		getParams := c.Query("get_params")
		massReq := corev1.MaasReq{
			MaasURL:   config.GetConfig().MaasUrl,
			APIKey:    config.GetConfig().MaasAPIKey,
			Version:   config.GetConfig().MaasAPIVersion,
			GetParams: getParams,
		}
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		rlt, err := cli.Maas().VMHostsDelete(&massReq)
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		_ = json.Unmarshal(rlt, &resp)
		gluehttp.Response(c, resp)
	}
}

// MachinesCreate Create a machine
func MachinesCreate(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		var resp map[string]string
		var val map[string]string
		_ = c.Bind(&val)
		massReq := corev1.MaasReq{
			MaasURL: config.GetConfig().MaasUrl,
			APIKey:  config.GetConfig().MaasAPIKey,
			Version: config.GetConfig().MaasAPIVersion,
			Value:   val,
		}
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		rlt, err := cli.Maas().MachineCreate(&massReq)
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		_ = json.Unmarshal(rlt, &resp)
		gluehttp.Response(c, resp)
	}
}

// MachinesDelete Delete a machine
func MachinesDelete(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		var resp map[string]string
		getParams := c.Query("get_params")
		massReq := corev1.MaasReq{
			MaasURL:   config.GetConfig().MaasUrl,
			APIKey:    config.GetConfig().MaasAPIKey,
			Version:   config.GetConfig().MaasAPIVersion,
			GetParams: getParams,
		}
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		rlt, err := cli.Maas().MachineDelete(&massReq)
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		_ = json.Unmarshal(rlt, &resp)
		gluehttp.Response(c, resp)
	}
}

// MachinesUpdate Update a machine,only the kernel can be changed temporarily
func MachinesUpdate(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		var resp maasentity.Machine
		var val model2.MachinesUpdateReq
		_ = c.Bind(&val)
		massReq := corev1.MaasReq{
			MaasURL:   config.GetConfig().MaasUrl,
			APIKey:    config.GetConfig().MaasAPIKey,
			Version:   config.GetConfig().MaasAPIVersion,
			GetParams: val.SystemID,
			Value: map[string]string{
				"architecture":   val.Architecture,
				"min_hwe_kernel": val.MinHweKernel,
			},
		}
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		rlt, err := cli.Maas().MachineUpdate(&massReq)
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		_ = json.Unmarshal(rlt, &resp)
		gluehttp.Response(c, resp)
	}
}

// MachinesSearch List all machines
func MachinesSearch(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		var resp []maasentity.Machine
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		rlt, err := cli.Maas().MachinesSearch(&corev1.MaasReq{MaasURL: config.GetConfig().MaasUrl, APIKey: config.GetConfig().MaasAPIKey, Version: config.GetConfig().MaasAPIVersion})
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		_ = json.Unmarshal(rlt, &resp)
		gluehttp.Response(c, resp)
	}
}

// MachinesPowerOn Power on a machine
func MachinesPowerOn(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		var resp maasentity.Machine
		getParams := c.Query("get_params")
		// Judge whether it is frozen
		machine, err := storages.DefaultMachineStorage.FindOne(mysql.GetDB(), model2.MaasMachine{
			SystemId: getParams,
		})
		if err != nil {
			gluelog.Errorf("机器不存在", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		accountToHost, err := storages.DefaultAccountToHostStorage.FindOne(mysql.GetDB(), model2.MeteringAccountToHost{
			HostId: machine.Id,
		})
		if err != nil {
			gluelog.Errorf("机器与账户关联信息不存在", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		if accountToHost.Status == MachinePermissionStatusArrearage {
			err := errors.New("机器已冻结,无权操作")
			gluelog.Errorf(err.Error())
			gluehttp.ErrorResponse(c, *gluehttp.NewError(http.StatusUnauthorized, err.Error(), err))
			return
		}
		massReq := corev1.MaasReq{
			MaasURL:   config.GetConfig().MaasUrl,
			APIKey:    config.GetConfig().MaasAPIKey,
			Version:   config.GetConfig().MaasAPIVersion,
			GetParams: getParams,
		}
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		rlt, err := cli.Maas().MachinePowerOn(&massReq)
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		_ = json.Unmarshal(rlt, &resp)
		gluehttp.Response(c, resp)
	}
}

// MachinesPowerOff Power off a machine
func MachinesPowerOff(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		var resp maasentity.Machine
		getParams := c.Query("get_params")
		// Judge whether it is frozen
		machine, err := storages.DefaultMachineStorage.FindOne(mysql.GetDB(), model2.MaasMachine{
			SystemId: getParams,
		})
		if err != nil {
			gluelog.Errorf("机器不存在", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		accountToHost, err := storages.DefaultAccountToHostStorage.FindOne(mysql.GetDB(), model2.MeteringAccountToHost{
			HostId: machine.Id,
		})
		if err != nil {
			gluelog.Errorf("机器与账户关联信息不存在", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		if accountToHost.Status == MachinePermissionStatusArrearage {
			err := errors.New("机器已冻结,无权操作")
			gluelog.Errorf(err.Error())
			gluehttp.ErrorResponse(c, *gluehttp.NewError(http.StatusUnauthorized, err.Error(), err))
			return
		}
		massReq := corev1.MaasReq{
			MaasURL:   config.GetConfig().MaasUrl,
			APIKey:    config.GetConfig().MaasAPIKey,
			Version:   config.GetConfig().MaasAPIVersion,
			GetParams: getParams,
		}
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		rlt, err := cli.Maas().MachinePowerOff(&massReq)
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		_ = json.Unmarshal(rlt, &resp)
		gluehttp.Response(c, resp)
	}
}

// ResourceListNew List all resources
func ResourceListNew(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		var resp []model2.Resource
		accountID := c.Query("account_id")
		db := mysql.GetDB()
		machines, err := storages.DefaultMachineStorage.FindList(db, model2.MaasMachine{}, accountID)
		if err != nil {
			gluelog.Errorf("查询计算资源信息失败", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		for _, machine := range machines {
			var machineType string
			if machine.Type == MachineTypeVMHost {
				machineType = TYPEVIRTUALCN
			} else if machine.Type == MachineTypePhysicalMachine {
				machineType = TYPEVMHOSTCN
			} else {
				machineType = ""
			}
			accountToHost, err := storages.DefaultAccountToHostStorage.FindOne(db, model2.MeteringAccountToHost{HostId: machine.Id})
			if err != nil {
				gluelog.Errorf("查询账户关联信息失败", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			resp = append(resp, model2.Resource{
				ID:           machine.SystemId,
				Name:         machine.HostName,
				Type:         machineType,
				CoresTotal:   int(machine.CpuCount),
				CoresUsed:    int(machine.CpuCount),
				MemoryTotal:  int(machine.Memory),
				MemoryUsed:   int(machine.Memory),
				StorageTotal: int(machine.Storage),
				StorageUsed:  int(machine.Storage),
				Status:       NodeStatusToStringStatus(node.Status(machine.Status)),
				IsFrozen:     accountToHost.Status,
				CreatedAt:    machine.CreatedAt,
			})
		}
		gluehttp.Response(c, resp)
	}
}

// ResourceList List all resources
func ResourceList(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		name := c.Query("name")
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		// 1.Search all machines
		var machines []maasentity.Machine
		rlt, err := cli.Maas().MachinesSearch(&corev1.MaasReq{MaasURL: config.GetConfig().MaasUrl, APIKey: config.GetConfig().MaasAPIKey, Version: config.GetConfig().MaasAPIVersion})
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		_ = json.Unmarshal(rlt, &machines)
		// 2.Data organization
		var resources []model2.Resource
		for _, machine := range machines {
			//Judge whether it is a virtual machine or a vm host
			var machineType string
			for _, tag := range machine.TagNames {
				if tag == TYPEVMHOSTEN {
					machineType = TYPEVMHOSTCN
					break
				} else if tag == TYPEVIRTUALEN {
					machineType = TYPEVIRTUALCN
					break
				}
			}
			resources = append(resources, model2.Resource{
				ID:           machine.SystemID,
				Name:         machine.Hostname,
				Type:         machineType,
				CoresTotal:   machine.CPUCount,
				CoresUsed:    machine.CPUCount,
				MemoryTotal:  machine.Memory,
				MemoryUsed:   machine.Memory,
				StorageTotal: int(machine.Storage),
				StorageUsed:  int(machine.Storage),
				Status:       NodeStatusToStringStatus(machine.Status),
			})
		}
		// 3.Response organization
		if name != "" {
			var resources2 []model2.Resource
			for _, resource := range resources {
				if strings.Contains(resource.Name, name) {
					resources2 = append(resources2, resource)
				}
			}
			resources = resources2
		}
		gluehttp.Response(c, resources)
	}
}

// ResourcePage List all machines in the form of pages, which are not used for the time being
func ResourcePage(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		name := c.Query("name")
		pageNo := c.Query("page_number")
		pageSize := c.Query("page_size")
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		// 1.Search all machines
		var machines []maasentity.Machine
		rlt, err := cli.Maas().MachinesSearch(&corev1.MaasReq{MaasURL: config.GetConfig().MaasUrl, APIKey: config.GetConfig().MaasAPIKey, Version: config.GetConfig().MaasAPIVersion})
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		_ = json.Unmarshal(rlt, &machines)
		// 2.Data organization
		var resources []model2.Resource
		for _, machine := range machines {
			//Judge whether it is a virtual machine or a vm host
			var machineType string
			for _, tag := range machine.TagNames {
				if tag == TYPEVMHOSTEN {
					machineType = TYPEVMHOSTCN
					break
				} else if tag == TYPEVIRTUALEN {
					machineType = TYPEVIRTUALCN
					break
				}
			}
			resources = append(resources, model2.Resource{
				ID:           machine.SystemID,
				Name:         machine.Hostname,
				Type:         machineType,
				CoresTotal:   machine.CPUCount,
				CoresUsed:    machine.CPUCount,
				MemoryTotal:  machine.Memory,
				MemoryUsed:   machine.Memory,
				StorageTotal: int(machine.Storage),
				StorageUsed:  int(machine.Storage),
				Status:       NodeStatusToStringStatus(machine.Status),
			})
		}
		// 3.Response organization
		if name != "" {
			var resources2 []model2.Resource
			for _, resource := range resources {
				if strings.Contains(resource.Name, name) {
					resources2 = append(resources2, resource)
				}
			}
			resources = resources2
		}
		var pageRequest gluehttp.PageRequest
		var pageResp gluehttp.PageResponse
		pageResp.PageRequest = &pageRequest
		pageResp.SetPageResponse(len(resources), pageNo, pageSize)
		// 4.Paging
		var end int
		start := (pageResp.PageNo - 1) * pageResp.PageSize
		if pageResp.PageNo*pageResp.PageSize > pageResp.Total {
			end = pageResp.Total
		} else {
			end = pageResp.PageNo * pageResp.PageSize
		}
		resp := model2.ResourcePageResp{
			Page:      pageResp,
			Resources: resources[start:end],
		}
		gluehttp.Response(c, resp)
	}
}

// ResourceDetail Display the details of a machine
func ResourceDetail(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		id := c.Query("id")
		massReq := corev1.MaasReq{
			MaasURL:   config.GetConfig().MaasUrl,
			APIKey:    config.GetConfig().MaasAPIKey,
			Version:   config.GetConfig().MaasAPIVersion,
			GetParams: id,
		}
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}

		var machine maasentity.Machine
		rlt, err := cli.Maas().MachineSearch(&massReq)
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		_ = json.Unmarshal(rlt, &machine)
		var ipAddr string
		if machine.IPAddresses != nil {
			for k, ip := range machine.IPAddresses {
				if k != 0 && k != len(machine.IPAddresses)-1 {
					ipAddr = ipAddr + ip.String() + ","
				} else {
					ipAddr = ipAddr + ip.String()
				}
			}
		}
		// Processing tags
		var machineType string
		var region string
		for _, tag := range machine.TagNames {
			if tag == TYPEVMHOSTEN {
				machineType = TYPEVMHOSTCN
			}
			if tag == TYPEVIRTUALEN {
				machineType = TYPEVIRTUALCN
			}
			if strings.Contains(tag, TAGREGION) {
				region = tag[strings.Index(tag, TAGSEPARATOR)+1:]
			}
		}
		resp := model2.ResourceInfoResp{
			ID:           machine.SystemID,
			Name:         machine.Hostname,
			ResourceType: machineType,
			Status:       NodeStatusToStringStatus(machine.Status),
			Region:       region,
			Tags:         machine.TagNames,
			IPAddr:       ipAddr,
			Core:         machine.CPUCount,
			Memory:       machine.Memory,
			Architecture: []string{machine.Architecture},
			Image:        machine.OSystem + "/" + machine.DistroSeries,
			Storage:      int(machine.Storage),
			NetWork:      "",
		}
		gluehttp.Response(c, resp)
	}
}

// ResourceBatchOperation Batch operation machine, mainly used for machine on/off operation
func ResourceBatchOperation(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		var resourceBatchOperationReq model2.ResourceBatchOperationReq
		_ = c.Bind(&resourceBatchOperationReq)
		massReq := corev1.MaasReq{
			MaasURL: config.GetConfig().MaasUrl,
			APIKey:  config.GetConfig().MaasAPIKey,
			Version: config.GetConfig().MaasAPIVersion,
		}
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		// 1.Processing machines
		if len(resourceBatchOperationReq.MachinesID) != 0 {
			switch resourceBatchOperationReq.Operation {
			case OPERATIONPOWERON:
				var resourceBatchOperationResp model2.ResourceBatchOperationResp
				for _, id := range resourceBatchOperationReq.MachinesID {
					massReq.GetParams = id
					_, err := cli.Maas().MachinePowerOn(&massReq)
					if err != nil {
						resourceBatchOperationResp.MachinesID["fail"] = append(resourceBatchOperationResp.MachinesID["fail"], id)
					} else {
						resourceBatchOperationResp.MachinesID["success"] = append(resourceBatchOperationResp.MachinesID["success"], id)
					}
				}
				gluehttp.Response(c, resourceBatchOperationResp)
			case OPERATIONPOWEROFF:
				var resourceBatchOperationResp model2.ResourceBatchOperationResp
				for _, id := range resourceBatchOperationReq.MachinesID {
					massReq.GetParams = id
					_, err := cli.Maas().MachinePowerOff(&massReq)
					if err != nil {
						resourceBatchOperationResp.MachinesID["fail"] = append(resourceBatchOperationResp.MachinesID["fail"], id)
					} else {
						resourceBatchOperationResp.MachinesID["success"] = append(resourceBatchOperationResp.MachinesID["success"], id)
					}
				}
				gluehttp.Response(c, resourceBatchOperationResp)
			default:
				err = errors.New("operation is not supported")
				gluelog.Errorf("operation is empty", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
		}
		//TODO Support VMHosts
	}
}

// MachinesDetails Get machine details
func MachinesDetails(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		var resp []map[string]interface{}
		id := c.Query("get_params")
		massReq := corev1.MaasReq{
			MaasURL:   config.GetConfig().MaasUrl,
			APIKey:    config.GetConfig().MaasAPIKey,
			Version:   config.GetConfig().MaasAPIVersion,
			GetParams: id,
		}
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		rlt, err := cli.Maas().MachineDetails(&massReq)
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		_ = json.Unmarshal(rlt, &resp)
		gluehttp.Response(c, resp)
	}
}

// TagsUpdateNodes Update machine tags
func TagsUpdateNodes(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		var resp map[string]int
		var val map[string]string
		id := c.Query("get_params")
		_ = c.Bind(&val)
		massReq := corev1.MaasReq{
			MaasURL:   config.GetConfig().MaasUrl,
			APIKey:    config.GetConfig().MaasAPIKey,
			Version:   config.GetConfig().MaasAPIVersion,
			GetParams: id,
			Value:     val,
		}
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		rlt, err := cli.Maas().TagsUpdateNodes(&massReq)
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		_ = json.Unmarshal(rlt, &resp)
		gluehttp.Response(c, resp)
	}
}

// MachinesDeploy Use the default image to deploy a ready machine
func MachinesDeploy(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		var resp maasentity.Machine
		var val model2.MachinesDeployReq
		_ = c.Bind(&val)
		massReq := corev1.MaasReq{
			MaasURL:   config.GetConfig().MaasUrl,
			APIKey:    config.GetConfig().MaasAPIKey,
			Version:   config.GetConfig().MaasAPIVersion,
			GetParams: val.SystemID,
			Value:     map[string]string{},
		}
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		rlt, err := cli.Maas().MachineDeploy(&massReq)
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		_ = json.Unmarshal(rlt, &resp)
		gluehttp.Response(c, resp)
	}
}

// VMHostsTags List all tags of hosts
func VMHostsTags(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		resp := model2.VMHostsTagsResp{
			Region:  make(map[string]*model2.VMHostsTags),
			Tags:    make(map[string]*model2.VMHostsTags),
			NetWork: make(map[string]*model2.VMHostsTags),
		}
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		// 1.Search available vm hosts
		var vmHosts []maasentity.VMHost
		rlt, err := cli.Maas().VMHostsSearch(&corev1.MaasReq{MaasURL: config.GetConfig().MaasUrl, APIKey: config.GetConfig().MaasAPIKey, Version: config.GetConfig().MaasAPIVersion})
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		_ = json.Unmarshal(rlt, &vmHosts)
		// 2.Search available machines through vm hosts and get tags
		for _, vmHost := range vmHosts {
			massReq := corev1.MaasReq{
				MaasURL:   config.GetConfig().MaasUrl,
				APIKey:    config.GetConfig().MaasAPIKey,
				Version:   config.GetConfig().MaasAPIVersion,
				GetParams: vmHost.Host.SystemID,
			}
			rlt, err := cli.Maas().MachineSearch(&massReq)
			if err != nil {
				gluelog.Errorf("failed to request rpc", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			var machine maasentity.Machine
			_ = json.Unmarshal(rlt, &machine)
			// 3.Set Tags
			for _, tag := range machine.TagNames {
				// Set region tag
				if strings.Contains(tag, TAGREGION) {
					name := tag[strings.Index(tag, TAGSEPARATOR)+1:]
					if resp.Region[name] == nil {
						resp.Region[name] = &model2.VMHostsTags{
							VMHostID: []int{vmHost.ID},
						}
					} else {
						resp.Region[name].VMHostID = append(resp.Region[name].VMHostID, vmHost.ID)
					}
				} else if strings.Contains(tag, TAGPODCONSOLELOGGING) ||
					strings.Contains(tag, TYPEVIRTUALEN) ||
					strings.Contains(tag, TYPEVMHOSTEN) {
				} else {
					// Set tags tag
					if resp.Tags[tag] == nil {
						resp.Tags[tag] = &model2.VMHostsTags{
							VMHostID: []int{vmHost.ID},
						}
					} else {
						resp.Tags[tag].VMHostID = append(resp.Tags[tag].VMHostID, vmHost.ID)
					}
				}
			}
			// Set network tag
			for _, discovered := range machine.InterfaceSet {
				for _, link := range discovered.Links {
					if resp.NetWork[link.Subnet.Name] == nil {
						resp.NetWork[link.Subnet.Name] = &model2.VMHostsTags{
							VMHostID: []int{vmHost.ID},
						}
					} else {
						resp.NetWork[link.Subnet.Name].VMHostID = append(resp.NetWork[link.Subnet.Name].VMHostID, vmHost.ID)
					}
				}
			}
			// RemoveDuplicates
			for _, v := range resp.NetWork {
				v.VMHostID = RemoveDuplicate(&v.VMHostID)
			}
		}
		// 3.Data organization
		gluehttp.Response(c, resp)
	}
}

// BareMachineList List all bare machines
func BareMachineList(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		var resp []model2.BareMachineListResp
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		// 1.Search available vm hosts
		var vmHosts []maasentity.VMHost
		rlt, err := cli.Maas().VMHostsSearch(&corev1.MaasReq{MaasURL: config.GetConfig().MaasUrl, APIKey: config.GetConfig().MaasAPIKey, Version: config.GetConfig().MaasAPIVersion})
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		_ = json.Unmarshal(rlt, &vmHosts)
		// 2.Search all machines
		for _, vmHost := range vmHosts {
			massReq := corev1.MaasReq{
				MaasURL:   config.GetConfig().MaasUrl,
				APIKey:    config.GetConfig().MaasAPIKey,
				Version:   config.GetConfig().MaasAPIVersion,
				GetParams: vmHost.Host.SystemID,
			}
			rlt, err := cli.Maas().MachineSearch(&massReq)
			if err != nil {
				gluelog.Errorf("failed to request rpc", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			var machine maasentity.Machine
			_ = json.Unmarshal(rlt, &machine)
			// Set tags
			var tags []string
			for _, tag := range machine.TagNames {
				// Set region tag
				if strings.Contains(tag, TAGREGION) {
					name := tag[strings.Index(tag, TAGSEPARATOR)+1:]
					tags = append(tags, name)
				} else if strings.Contains(tag, TAGPODCONSOLELOGGING) ||
					strings.Contains(tag, TYPEVIRTUALEN) ||
					strings.Contains(tag, TYPEVMHOSTEN) {
				} else {
					// Set tags tag
					tags = append(tags, tag)
				}
			}
			// Set network tag
			for _, discovered := range machine.InterfaceSet {
				for _, link := range discovered.Links {
					tags = append(tags, link.Subnet.Name)
				}
			}
			// RemoveDuplicates
			tags = SliceRemoveDuplicates(tags)
			// Data organization
			resp = append(resp, model2.BareMachineListResp{
				ID:           machine.SystemID,
				Name:         machine.Hostname,
				Tags:         tags,
				Core:         machine.CPUCount,
				Memory:       machine.Memory,
				Architecture: machine.Architecture,
				Image:        machine.OSystem,
				Version:      machine.DistroSeries,
				Kernel:       machine.MinHWEKernel,
				Storage:      int(machine.Storage),
				NetWork:      "",
			})
		}
		gluehttp.Response(c, resp)
	}
}

// DatabaseInit Initialize the relevant data in the database,
// including the data of machine vm host and associated tables.
func DatabaseInit(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 1.Get query parameters
		userID := c.Query("id")
		massReq := corev1.MaasReq{
			MaasURL:   config.GetConfig().MaasUrl,
			APIKey:    config.GetConfig().MaasAPIKey,
			Version:   config.GetConfig().MaasAPIVersion,
			GetParams: userID,
		}
		// 2.Call rpc
		agentGuid := config.GetConfig().MaasAgentName
		cli, err := agent.NewAgent(&rpc.Config{
			Target:  agentGuid,
			NatsUrl: config.GetConfig().NatsServerAddr,
		})
		if err != nil {
			gluelog.Errorf("rpc url is empty", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		rlt, err := cli.Maas().MachineSearch(&massReq)
		if err != nil {
			gluelog.Errorf("failed to request rpc", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		// 3. Data organization
		var machine maasentity.Machine
		var tags string
		_ = json.Unmarshal(rlt, &machine)
		for _, tag := range machine.TagNames {
			tags = tags + tag + ","
		}
		machineReq := model2.MaasMachine{
			HostName:     machine.Hostname,
			SystemId:     machine.SystemID,
			CpuCount:     uint(machine.CPUCount),
			Memory:       uint(machine.Memory),
			Storage:      uint64(machine.Storage),
			Type:         MachineTypeVMHost,
			Architecture: machine.Architecture,
			Osystem:      machine.OSystem,
			HweKernel:    machine.HWEKernel,
			TagNames:     tags,
			Status:       int(machine.Status),
			Locked:       machine.Locked,
			PowerState:   machine.PowerState,
		}
		// 4. Insert data
		db := mysql.GetDB()
		machineID, err := storages.DefaultMachineStorage.Insert(db, machineReq)
		if err != nil {
			gluelog.Errorf("failed to create machine", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		gluehttp.Response(c, machineID)
	}
}

func MaasRouter(router *gin.RouterGroup) func(app *app.Application) {
	return func(a *app.Application) {
		v1 := router.Group("/maas")
		{
			// 1.VM-host
			vmHostsApi := v1.Group("/vm-hosts")
			{
				vmHostsApi.GET("", VMHostsSearch(a))
				vmHostsApi.POST("", VMHostsCreate(a))
				vmHostsApi.DELETE("", VMHostsDelete(a))
				vmHostsApi.GET("/tags", VMHostsTags(a))
			}
			// 2.Machine
			machinesApi := v1.Group("/machines")
			{
				machinesApi.GET("", MachinesSearch(a))
				machinesApi.POST("", MachinesCreate(a))
				machinesApi.PUT("", MachinesUpdate(a))
				machinesApi.GET("/details", MachinesDetails(a))
				machinesApi.POST("/power-on", MachinesPowerOn(a))
				machinesApi.POST("/power-off", MachinesPowerOff(a))
				machinesApi.POST("/deploy", MachinesDeploy(a))
			}

			// 3.Resource
			resourceApi := v1.Group("/resource")
			{
				resourceApi.DELETE("", MachinesDelete(a))
				resourceApi.GET("/list", ResourceListNew(a))
				resourceApi.GET("/detail", ResourceDetail(a))
				resourceApi.PUT("/batch_operation", ResourceBatchOperation(a))
				resourceApi.GET("/page", ResourcePage(a))
			}
			// 4.Configuration
			v1.POST("/tags/update_nodes", TagsUpdateNodes(a))
			// 5.Other
			v1.GET("/boot-resources", BootResourcesSearch(a))
			v1.GET("/subnets", SubNetsSearch(a))
			v1.GET("/bare_machines/list", BareMachineList(a))
			// 6.Database synchronization
			v1.GET("/database_init", DatabaseInit(a))
		}
	}
}

func init() {
	app.Router(MaasRouter)
}
